package com.isyscore.os.driver;

import com.isyscore.os.driver.core.driver.*;
import com.isyscore.os.driver.core.enums.AttributeOperationEnum;
import com.isyscore.os.driver.core.enums.ItemTypeEnum;
import com.isyscore.os.driver.core.enums.SerialCommunicationCheck;
import com.isyscore.os.driver.core.utils.CommConf;
import com.isyscore.os.driver.core.utils.SerialPortHelper;
import com.isyscore.os.driver.protocol.ProtocolRequestData;

import com.isyscore.os.driver.protocol.ProtocolResponseData;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.DatatypeConverter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import com.isyscore.os.driver.protocol.utils;

public class DianbiaoAccess extends DeviceDriverBase {
    private static final Logger logger = LoggerFactory.getLogger(DianbiaoAccess.class);

    public static Map<String, DeviceInfo> deviceInfoDevIdMap = new ConcurrentHashMap<>();

    private SerialPortHelper objSerial;


    public DianbiaoAccess() {
        super("35fe95dd361348c6964d7e691f8886f2",
                "",
                "DTSY450J-30(100A)",
                "1.0.0",
                "三相电子式预付费电能表DTSY450J-30(100A)",
                "xyq",
                true,
                "智能电表驱动",
                false);
    }

    public DriverFunctions initFunctions() {
        DriverFunctions driverFunctions = new DriverFunctions();

        DriverAttribute[] driverAttributes = new DriverAttribute[]{
                new DriverAttribute("tensionA","A相电压",""),
                new DriverAttribute("tensionB","B相电压",""),
                new DriverAttribute("tensionC","C相电压",""),
                new DriverAttribute("currentA","A相电流",""),
                new DriverAttribute("currentB","B相电流",""),
                new DriverAttribute("currentC","C相电流",""),
                new DriverAttribute("forwardPowerTotalEnergy","正向有功总电能",""),
        };

        
        driverFunctions.setDriverServices(Collections.emptyList());
        driverFunctions.setDriverAttributes(Arrays.asList(driverAttributes));
        driverFunctions.setDriverEvents(Collections.emptyList());
        return driverFunctions;
    }

    public List<ConnectParam> initConnectParams() {
        return Collections.emptyList();
    }


    @Override
    public int initial(InitialParam driverParam) {
        objSerial = new SerialPortHelper();
        COMParam com = driverParam.getComParam();
        String comName = com.getComName();
        logger.info("尝试打开串口设备：" + comName);

        //设置波特率，数据位，停止位，校验方式等
        CommConf objCommConf = new CommConf();
        objCommConf.setStrCommName(comName);
        objCommConf.setNBaudrate(com.getComBaudrate().getDesc());
        objCommConf.setNDataBit(com.getComData());
        objCommConf.setNParityBit(SerialCommunicationCheck.getNumber(com.getComCheck()));
        objCommConf.setNStopBit(com.getComStop());

        //打开串口设备
        if (!objSerial.openSerialPort(objCommConf)) {
            logger.error("打开串口设备失败： {} ", comName);
            return -1;

        }
        logger.info("打开串口设备成功: CommName[" + comName + "]" + " BaudRate[" + com.getComBaudrate() + "]");
        driverParam.getDeviceInfoList().forEach(deviceInfo -> {
            String devId = deviceInfo.getDevId();
            deviceInfoDevIdMap.put(devId, deviceInfo);
        });

        return 0;
    }

    @Override
    public int unInitial() {
        if (objSerial != null) {
            objSerial.closeSerialPort();
            objSerial = null;
        }
        return 0;
    }
    public DriverResult checkConnectParams(List<ConnectParam> params) {
        return DriverResult.SUCCESS;
    }

    public DriverResult checkFunction(ItemTypeEnum functionType, String functionName, String functionParam) {
        return DriverResult.SUCCESS;
    }

    public boolean canPackage(TupleFrame frame, TslAttributeAction attributeAction) {
        return false;
    }

    public DriverResult accessAttribute(TupleFrame frame) {
        List<TslAttributeAction> actions = frame.getAttributeActions();
        if (actions.size() <= 0) {
            logger.error("无效的action");
            return DriverResult.FAILED;
        }
        TslAttributeAction action = actions.get(0);
        String driverFunctionName = action.getDriverFunctionName();
        String devId = action.getDevId();
        DeviceInfo deviceInfo = deviceInfoDevIdMap.get(devId);
        String addressNum = deviceInfo.getExtra();
        
        String res = null;
        try {
            switch (driverFunctionName){
                case "A相电压":
                    res = getTension(addressNum,"A");
                    break;
                case "B相电压":
                    res = getTension(addressNum,"B");
                    break;
                case "C相电压":
                    res = getTension(addressNum,"C");
                    break;
                case "A相电流":
                    res = getCurrent(addressNum,"A");
                    break;
                case "B相电流":
                    res = getCurrent(addressNum,"B");
                    break;
                case "C相电流":
                    res = getCurrent(addressNum,"C");
                    break;
                case "正向有功总电能":
                    res = getforwardPowerTotalEnergy(addressNum);
                    break;
                default:
                    logger.error("驱动功能不存在");
                    return DriverResult.NO_IMPLEMENT;
            }
            for (TslAttributeAction ac:actions){
                ac.setValue(res);
            }
        }catch (Exception e){
            logger.error(e.getMessage());
            return DriverResult.FAILED;
        }


        return DriverResult.SUCCESS;

    }

    public DriverResult accessService(TslServiceAction serviceAction) {
            return DriverResult.SUCCESS;
    }

    private String getTension(String addressNum,String ABC) throws IOException {
        byte[] bAddressNum = utils.hexStringToBytes(addressNum);
        utils.reverse(bAddressNum);
        byte controlCode = 0x01;
        byte dataLen = 0x02;
        byte[] dataIdentifier;
        if (ABC.equals("A")){
            dataIdentifier = new  byte[]{0x11, (byte) 0xB6};
        } else if (ABC.equals("B")){
            dataIdentifier = new  byte[]{0x12, (byte) 0xB6};
        }else {
            dataIdentifier = new  byte[]{0x13, (byte) 0xB6};
        }

        ProtocolRequestData protocolRequestData = new ProtocolRequestData(bAddressNum,controlCode,dataLen,dataIdentifier);
        byte[] reqData = protocolRequestData.encode();
        byte[] response = sendAndRecv(reqData, 1000);
        if (response == null){
            return null;
        }
        ProtocolResponseData protocolResponseData = protocolDecode(response);
        byte[] data = protocolResponseData.getData();
        utils.reverse(data);
        for (int i = 0; i < data.length; i++) {
            data[i] = (byte) (data[i] - 0x33);
        }
        String hData = DatatypeConverter.printHexBinary(data);
        BigDecimal value = BigDecimal.valueOf(Long.parseLong(hData)).multiply(BigDecimal.valueOf(0.1));
        return String.valueOf(value);
    }

    private String getCurrent(String addressNum,String ABC) throws IOException {
        byte[] bAddressNum = utils.hexStringToBytes(addressNum);
        utils.reverse(bAddressNum);
        byte controlCode = 0x01;
        byte dataLen = 0x02;
        byte[] dataIdentifier;
        if (ABC.equals("A")){
            dataIdentifier = new  byte[]{0x21, (byte) 0xB6};
        } else if (ABC.equals("B")){
            dataIdentifier = new  byte[]{0x22, (byte) 0xB6};
        }else {
            dataIdentifier = new  byte[]{0x23, (byte) 0xB6};
        }

        ProtocolRequestData protocolRequestData = new ProtocolRequestData(bAddressNum,controlCode,dataLen,dataIdentifier);
        byte[] reqData = protocolRequestData.encode();
        byte[] response = sendAndRecv(reqData, 1000);
        if (response == null){
            return null;
        }
        ProtocolResponseData protocolResponseData = protocolDecode(response);
        byte[] data = protocolResponseData.getData();
        utils.reverse(data);
        for (int i = 0; i < data.length; i++) {
            data[i] = (byte) (data[i] - 0x33);
        }
        String hData = DatatypeConverter.printHexBinary(data);
        BigDecimal value = BigDecimal.valueOf(Long.parseLong(hData)).multiply(BigDecimal.valueOf(0.01));
        return String.valueOf(value);
    }

    private String getforwardPowerTotalEnergy(String addressNum) throws IOException {
        byte[] bAddressNum = utils.hexStringToBytes(addressNum);
        utils.reverse(bAddressNum);
        byte controlCode = 0x01;
        byte dataLen = 0x02;
        byte[] dataIdentifier = new  byte[]{0x10, (byte) 0x90};

        ProtocolRequestData protocolRequestData = new ProtocolRequestData(bAddressNum,controlCode,dataLen,dataIdentifier);
        byte[] reqData = protocolRequestData.encode();
        byte[] response = sendAndRecv(reqData, 1000);
        if (response == null){
            return null;
        }
        ProtocolResponseData protocolResponseData = protocolDecode(response);
        byte[] data = protocolResponseData.getData();
        utils.reverse(data);
        for (int i = 0; i < data.length; i++) {
            data[i] = (byte) (data[i] - 0x33);
        }
        String hData = DatatypeConverter.printHexBinary(data);
        BigDecimal value = BigDecimal.valueOf(Long.parseLong(hData)).multiply(BigDecimal.valueOf(0.01));
        return String.valueOf(value);
    }


    private ProtocolResponseData protocolDecode(byte[] response){
        ByteBuf buffer = Unpooled.wrappedBuffer(response);
        while (true){
            byte header = buffer.readByte();
            if (header == 0x68){
                break;
            }
        }
        byte[] addressNum = new byte[6];
        buffer.readBytes(addressNum);
        buffer.readByte();
        byte controlCode = buffer.readByte();
        byte dataLength = buffer.readByte();
        byte[] dataIdentifier = new byte[2];
        buffer.readBytes(dataIdentifier);
        byte[] data = new byte[dataLength-2];
        buffer.readBytes(data);
        byte checkCode = buffer.readByte();
        buffer.readByte();

        ProtocolResponseData protocolResponseData = new ProtocolResponseData();
        protocolResponseData.setControlCode(controlCode);
        protocolResponseData.setDataLength(dataLength);
        protocolResponseData.setAddressNum(addressNum);
        protocolResponseData.setData(data);
        protocolResponseData.setCheckCode(checkCode);


        return protocolResponseData;

    }

    public byte[] sendAndRecv(byte[] buffer, int timeout) {
        byte[] res = null;
        try {
            byte[] preData = new byte[]{(byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE};
            objSerial.sendData(preData);
            logger.info("发送控制报文：{}", DatatypeConverter.printHexBinary(buffer));
            if (!objSerial.sendData(buffer)) {
                logger.error("发送报文失败");
                return null;
            }
            ByteArrayOutputStream output = new ByteArrayOutputStream();

            while (true){
                byte[] response = objSerial.waitAndRead(timeout);
                if (response != null){
                    output.write(response);
                }else {
                    break;
                }
            }
            res = output.toByteArray();
            logger.info("收到响应报文：{}", DatatypeConverter.printHexBinary(res));
        } catch (Exception e) {
            logger.error(e.getMessage());
        }


        return res;
    }

    private void notifyEvent(String devId,String eventIdentifier) {
        TslEventAction action = new TslEventAction();
        action.setDevId(devId);
        action.setIdentifier(eventIdentifier);
        listener.onEvent(action);
    }


    public static void main(String[] args) throws IOException {
        DianbiaoAccess dianbiaoAccess = new DianbiaoAccess();
        dianbiaoAccess.exportDescFile(null);
        String addressNum = "000021089622";
        //设置波特率，数据位，停止位，校验方式等
        CommConf objCommConf = new CommConf();
        objCommConf.setStrCommName("COM1");
        dianbiaoAccess.objSerial = new SerialPortHelper();
        boolean x = dianbiaoAccess.objSerial.openSerialPort(objCommConf);
        String xx = dianbiaoAccess.getTension(addressNum,"A");

        System.out.println(1);

    }
}
